﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;
using XUCore.Extensions;
using XUCore.Helpers;
using XUCore.WeChat.Enterprise;
using XUCore.WeChat.Enterprise.Net.SuiteServerMessages;
using XUCore.WeChat.Enterprise.Net.ServerMessages;
using XUCore.WeChat.Helper;
using XUCore.WeChat.Net.ServerMessages;

namespace XUCore.WeChat.Net.Controllers
{
    /// <summary>
    /// 服务器事件处理控制器
    /// </summary>
    [AllowAnonymous]
    [Route("WeChat")]
    public class WeChatEventController : ControllerBase
    {
        private readonly ILogger<WeChatEventController> logger;
        private readonly IServiceProvider serviceProvider;

        public WeChatEventController(IServiceProvider serviceProvider, ILogger<WeChatEventController> logger)
        {
            this.serviceProvider = serviceProvider;
            this.logger = logger;
        }

        #region 微信公众号服务端接入

        /// <summary>
        /// 【微信公众号】检查签名
        /// </summary>
        /// <param name="app">应用标识，应用配置请先固定参数</param>
        /// <param name="signature">The signature<see cref="string"/></param>
        /// <param name="timestamp">The timestamp<see cref="string"/></param>
        /// <param name="nonce">The nonce<see cref="string"/></param>
        /// <param name="echostr">The echostr<see cref="string"/></param>
        /// <returns>The <see cref="ActionResult"/></returns>
        [HttpGet("Events")]
        public ActionResult CheckSignature(string app, string signature, string timestamp, string nonce, string echostr)
        {
            var serverMessageHandler = serviceProvider.GetRequiredService<ServerMessageHandler>();

            if (serverMessageHandler == null)
            {
                logger.LogError($"请先注册 ServerMessageHandler 服务器消息处理类");
                throw new WeChatSdkException("请先注册 ServerMessageHandler 服务器消息处理类");
            }

            return Content(serverMessageHandler.CheckSignature(app, signature, timestamp, nonce) ? echostr : "验证签名出错！");
        }

        /// <summary>
        /// 【微信公众号】处理服务器事件消息
        /// </summary>
        /// <param name="app">应用标识，应用配置请先固定参数</param>
        /// <param name="signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="echostr"></param>
        /// <param name="msg_signature"></param>
        /// <returns></returns>
        [HttpPost("Events")]
        public async Task<ActionResult> HandleMessage(string app, string signature, string timestamp, string nonce, string echostr, string msg_signature)
        {
            var serverMessageHandler = serviceProvider.GetRequiredService<ServerMessageHandler>();
            var wxFuncs = serviceProvider.GetRequiredService<WeChatFuncs>();

            if (serverMessageHandler == null)
            {
                logger.LogError($"请先注册 ServerMessageHandler 服务器消息处理类");
                throw new WeChatSdkException("请先注册 ServerMessageHandler 服务器消息处理类");
            }

            logger.LogDebug("正在处理微信服务器消息...");

            using var reader = new StreamReader(Request.Body);
            var xmlStr = await reader.ReadToEndAsync();
            //检查签名
            if (!serverMessageHandler.CheckSignature(app, signature, timestamp, nonce))
            {
                logger.LogWarning($"签名验证出错:\n signature:{signature}\t timestamp:{timestamp}\t nonce:{nonce}\t echostr:{echostr}");
                return BadRequest("签名验证出错!");
            }

            logger.LogDebug("签名校验完成...");

            //安全模式
            var opt = wxFuncs.GetWeChatOptions(app);

            if (opt == null)
                throw new WeChatSdkException($"{nameof(WeChatEventController)}，App:{app}，获取微信配置失败。");

            if (opt.EncodingAESKey.NotEmpty())
            {
                var wxcpt = new WeChatCryptBizMessage(opt.Token, opt.EncodingAESKey, opt.AppId);
                var ret = wxcpt.Decrypt(msg_signature, timestamp, nonce, xmlStr, ref xmlStr);
                if (ret != WeChatCryptErrorCode.OK)
                    throw new WeChatSdkException($"安全模式解析文档，{ret.Description()},{Environment.NewLine}signature={signature}{Environment.NewLine}msg_signature={msg_signature}{Environment.NewLine}timestamp={timestamp}{Environment.NewLine}nonce={nonce}{Environment.NewLine}echostr={echostr}");
            }

            //处理事件消息
            var result = await serverMessageHandler.HandleMessage(xmlStr);
            if (result == null)
                return Ok();
            var toXml = result.ToXml();
            logger.LogDebug($"微信服务器事件处理成功，返回格式为:{Environment.NewLine}{toXml}");
            return Content(toXml);
        }

        /// <summary>
        /// jsapi 生成H5请求的页面签名
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpGet("Signature")]
        public async Task<WebSignatureResult> WebEnterpriseSignature([FromQuery] WebSignatureRequest request)
        {
            var webSignature = serviceProvider.GetRequiredService<IWeChatWebSignature>();

            return await webSignature.Create(request);
        }

        #endregion

        #region  企业微信服务端api接入

        /// <summary>
        /// 【企业微信】检查签名
        /// </summary>
        /// <param name="msg_signature">The signature<see cref="string"/></param>
        /// <param name="timestamp">The timestamp<see cref="string"/></param>
        /// <param name="nonce">The nonce<see cref="string"/></param>
        /// <param name="echostr">The echostr<see cref="string"/></param>
        /// <param name="suiteId">企业微信代开发的模板Id，没有不填，在url中固定配置</param>
        /// <param name="corpId">企业微信corpid，类似以appid<see cref="string"/>，在url中固定配置</param>
        /// <param name="agentId">企业微信应用id<see cref="int"/>，在url中固定配置</param>
        /// <returns>The <see cref="ActionResult"/></returns>
        [HttpGet("Enterprise/Events")]
        public ActionResult CheckSignature(string msg_signature, string timestamp, string nonce, string echostr, string suiteId, string corpId, int agentId)
        {
            var wxFuncs = serviceProvider.GetRequiredService<EnterpriseWeChatFuncs>();

            (var _, var _, EnterpriseWeChatAgent agent) = wxFuncs.GetEnterpriseWeChatAgentOptions(suiteId, corpId, agentId);

            var wxcpt = new WeChatCryptBizMessage(agent.Token, agent.EncodingAESKey, corpId);
            var sReplyEchoStr = string.Empty;
            var res = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr, ref sReplyEchoStr);

            return Content(sReplyEchoStr);
        }

        /// <summary>
        /// 【企业微信】处理服务器事件消息
        /// </summary>
        /// <param name="msg_signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="suiteId">企业微信代开发的模板Id，没有不填，在url中固定配置</param>
        /// <param name="corpId">企业微信corpid，类似以appid<see cref="string"/>，在url中固定配置</param>
        /// <param name="agentId">企业微信应用id<see cref="int"/>，在url中固定配置</param>
        /// <returns></returns>
        [HttpPost("Enterprise/Events")]
        public async Task<ActionResult> HandleMessage(string msg_signature, string timestamp, string nonce, string suiteId, string corpId, int agentId)
        {
            var serverMessageHandler = serviceProvider.GetRequiredService<EnterpriseServerMessageHandler>();
            var wxFuncs = serviceProvider.GetRequiredService<EnterpriseWeChatFuncs>();

            if (serverMessageHandler == null)
            {
                logger.LogError($"请先注册 EnterpriseServerMessageHandler 服务器消息处理类");
                throw new WeChatSdkException("请先注册 EnterpriseServerMessageHandler 服务器消息处理类");
            }

            logger.LogDebug($"正在处理企业微信服务器消息...corpId   {corpId}   agentId   {agentId}");

            using var reader = new StreamReader(Request.Body);
            var xmlStr = await reader.ReadToEndAsync();

            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.LoadXml(xmlStr);

            logger.LogDebug(xmlStr);

            //agentId = Conv.ToInt(xmlDocument.FirstChild["AgentID"].InnerText);

            (_, _, EnterpriseWeChatAgent agent) = wxFuncs.GetEnterpriseWeChatAgentOptions(suiteId, corpId, agentId);

            var wxcpt = new WeChatCryptBizMessage(agent.Token, agent.EncodingAESKey, corpId);
            var ret = wxcpt.Decrypt(msg_signature, timestamp, nonce, xmlStr, ref xmlStr);
            if (ret != WeChatCryptErrorCode.OK)
                throw new WeChatSdkException($"安全模式解析文档，{ret.Description()},{Environment.NewLine}msg_signature={msg_signature}{Environment.NewLine}timestamp={timestamp}{Environment.NewLine}nonce={nonce}");

            logger.LogDebug("签名校验完成...");

            //处理事件消息
            var result = await serverMessageHandler.HandleMessage(suiteId, corpId, agentId, xmlStr);
            if (result == null)
                return Ok();

            var toXml = result.ToXml();

            logger.LogDebug($"企业微信服务器事件处理成功，返回格式为:{Environment.NewLine}{toXml}");

            return Content(toXml);
        }

        /// <summary>
        /// jsapi 企业微信 H5 Agent 生成请求的页面签名
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpGet("Enterprise/Signature")]
        public async Task<EnterpriseWebSignatureResult> WebAgentSignature([FromQuery] EnterpriseWebSignatureRequest request)
        {
            var webSignature = serviceProvider.GetRequiredService<IEnterpriseWeChatWebSignature>();

            return await webSignature.Create(request);
        }

        #endregion

        //Agent development

        /// <summary>
        /// 【企业微信】代开发模板回调--检查签名
        /// </summary>
        /// <param name="msg_signature">The signature<see cref="string"/></param>
        /// <param name="timestamp">The timestamp<see cref="string"/></param>
        /// <param name="nonce">The nonce<see cref="string"/></param>
        /// <param name="echostr">The echostr<see cref="string"/></param>
        /// <param name="suiteId">第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id（对应于旧的以tj开头的套件id）；代开发应用以dk开头</param>
        /// <returns>The <see cref="ActionResult"/></returns>
        [HttpGet("Enterprise/Suite")]
        public ActionResult SuiteCheckSignature(string msg_signature, string timestamp, string nonce, string echostr, string suiteId)
        {
            var wxFuncs = serviceProvider.GetRequiredService<EnterpriseWeChatFuncs>();

            var opt = wxFuncs.GetEnterpriseWeChatOptions();

            var suite = opt.FirstOrDefault(c => c.SuiteId == suiteId);

            var wxcpt = new WeChatCryptBizMessage(suite.Token, suite.EncodingAESKey, suite.CorpId);
            var sReplyEchoStr = string.Empty;
            var res = wxcpt.VerifyURL(msg_signature, timestamp, nonce, echostr, ref sReplyEchoStr);

            return Content(sReplyEchoStr);
        }

        /// <summary>
        /// 【企业微信】代开发模板回调--处理服务器事件消息
        /// </summary>
        /// <param name="msg_signature"></param>
        /// <param name="timestamp"></param>
        /// <param name="nonce"></param>
        /// <param name="suiteId">第三方应用id或者代开发应用模板id。第三方应用以ww或wx开头应用id（对应于旧的以tj开头的套件id）；代开发应用以dk开头</param>
        /// <returns></returns>
        [HttpPost("Enterprise/Suite")]
        public async Task<ActionResult> SuiteHandleMessage(string msg_signature, string timestamp, string nonce, string suiteId)
        {
            try
            {
                var wxFuncs = serviceProvider.GetRequiredService<EnterpriseWeChatFuncs>();

                using var reader = new StreamReader(Request.Body);
                var xmlStr = await reader.ReadToEndAsync();

                var xmlDocument = new XmlDocument();
                xmlDocument.LoadXml(xmlStr);

                //logger.LogDebug($"解密前：{xmlStr}");

                var opt = wxFuncs.GetEnterpriseWeChatOptions();

                var suite = opt.FirstOrDefault(c => c.SuiteId == suiteId);

                var wxcpt = new WeChatCryptBizMessage(suite.Token, suite.EncodingAESKey, suite.CorpId);
                var ret = wxcpt.Decrypt(msg_signature, timestamp, nonce, xmlStr, ref xmlStr);

                var content = new XmlDocument();
                content.LoadXml(xmlStr);

                logger.LogDebug($"【企业微信】代开发模板回调--处理服务器事件消息，解密后：\r\n{xmlStr}");

                var infoType = content.FirstChild["InfoType"].InnerText;

                //https://developer.work.weixin.qq.com/document/path/97174#14951 help

                var ticketManager = serviceProvider.GetRequiredService<IEnterpriseWeChatSuiteTicketManager>();

                var suiteEventHandler = serviceProvider.GetRequiredService<IEnterpriseWeChatSuiteEventHandler>();

                if (suiteEventHandler == null)
                {
                    logger.LogError($"请先注册 IEnterpriseWeChatSuiteEventHandler 代开发模板服务器消息处理类");
                    throw new WeChatSdkException("请先注册 IEnterpriseWeChatSuiteEventHandler 代开发模板服务器消息处理类");
                }

                switch (infoType)
                {
                    case "suite_ticket":
                        {
                            var _suiteId = content.FirstChild["SuiteId"].InnerText;
                            var suiteTicket = content.FirstChild["SuiteTicket"].InnerText;

                            ticketManager.SetTicket(_suiteId, suiteTicket);

                            logger.LogDebug($"{_suiteId} Ticket {suiteTicket}");
                        }
                        break;
                    case "create_auth":
                    case "reset_permanent_code":
                        {
                            var _suiteId = content.FirstChild["SuiteId"].InnerText;
                            var authCode = content.FirstChild["AuthCode"].InnerText;

                            var permanentCode = await ticketManager.GetPermanentAsync(_suiteId, authCode);

                            await suiteEventHandler.ExecutePermanentCodeEventHandler(suiteId, permanentCode);
                        }
                        break;
                    default:
                        {
                            await suiteEventHandler.ExecuteEventHandler(suiteId, infoType, xmlStr);
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                logger.LogError($"回调异常：{ex.Message}");
            }

            return Content("success");
        }
    }
}